package com.github.zyf.ioaj.export;

import com.github.zyf.ioaj.algorithm.AbstractAlgorithm;
import com.github.zyf.ioaj.annotation.internal.Algorithm;
import com.github.zyf.ioaj.constant.FileType;
import com.github.zyf.ioaj.exception.AlgorithmClassParserException;
import com.github.zyf.ioaj.exception.constant.AlgorithmClassParserExceptionType;

import java.lang.reflect.Field;

/**
 * <h1>报告</h1>
 * <p>当算法运行完成后，可以使用{@link AbstractAlgorithm#report(String, String, String, FileType)}方法加载报告，所返回的对象便是当前类</p>
 * <p>
 * 报告只会解析添加了如下三个注解的字段：
 * <ul>
 *     <li>{@link com.github.zyf.ioaj.annotation.internal.BasicParam}</li>
 *     <li>{@link com.github.zyf.ioaj.annotation.internal.InputParam}</li>
 *     <li>{@link com.github.zyf.ioaj.annotation.internal.OutputParam}</li>
 * </ul>
 * 报告的大小一般比较小，如果有需要建议使用
 *
 * @author zyf
 */
public class Report extends AbstractExport {

    private ContentBuilder contentBuilder;

    public Report(AbstractAlgorithm algorithm, String author, String message, String title, FileType fileType) {
        super(algorithm, author, message, title, fileType);
    }

    public ContentBuilder getContentBuilder() {
        return contentBuilder;
    }

    /**
     * 构建加载器内容
     *
     * @param contentBuilder 指定内容加载器
     * @return 加载器内容
     */
    private String buildContent(ContentBuilder contentBuilder) {
        // 获取算法所在类
        Class<?> clazz = getAlgorithm().getClass();
        // 检验是否标有Algorithm注解，否则抛出异常
        if (!clazz.isAnnotationPresent(Algorithm.class)) {
            throw new AlgorithmClassParserException(AlgorithmClassParserExceptionType.ALGORITHM_NOT_ANNOTATED);
        }
        // 从子类到父类层层检验是否有字段属于算法参数（至少有一个即可），否则抛出异常
        boolean isAnnotated = false;
        while (clazz != null) {
            // 扫描类
            boolean b = scanClass(clazz, getAlgorithm(), contentBuilder);
            if (b) isAnnotated = true;
            // 查找父类
            clazz = clazz.getSuperclass();
        }
        if (!isAnnotated)
            throw new AlgorithmClassParserException(AlgorithmClassParserExceptionType.ALGORITHM_PARAM_NOT_ANNOTATED);
        else {
            contentBuilder.complete(getAlgorithm().getClass());
            setLoaded();
        }
        return contentBuilder.toString();
    }

    /**
     * 扫描类（包括其父类），用以解析相关数据
     *
     * @param algorithm      需要扫描的类
     * @param contentBuilder 内容加载器
     */
    private boolean scanClass(Class<?> clazz, AbstractAlgorithm algorithm, ContentBuilder contentBuilder) {
        // 用以检查字段是否被注解，如果扫描了所有字段，一个都没注解，最后会抛出异常
        boolean isAnnotated = false;
        // 遍历所有字段
        for (Field field : clazz.getDeclaredFields()) {
            // 允许字段在访问时可以被检查
            field.setAccessible(true);
            // 判断字段是否添加了对应的注解
            boolean b = scanBasicParam(algorithm, field, contentBuilder)
                    || scanInputParam(algorithm, field, contentBuilder)
                    || scanOutputParam(algorithm, field, contentBuilder);
            if (b) isAnnotated = true;
        }
        return isAnnotated;
    }

    /**
     * 将报告输出为字符串
     *
     * @return {@link #getFileType()} 格式字符串
     */
    @Override
    public String export() {
        switch (getFileType()) {
            case TXT:
                contentBuilder = new TxtContentBuilder(getTitle(), getAuthor(), getMessage());
                return buildContent(contentBuilder);
            case JSON:
            default:
                contentBuilder = new JsonContentBuilder(getTitle(), getAuthor(), getMessage());
                return buildContent(contentBuilder);
        }
    }
}
